Last execution time: 13/03/2025 05:39:48
Get data
Products type filter
explore_types = ['frutas', 'lacteos', 'verduras', 'embutidos', 'panaderia', 'desayuno', 'congelados', 'abarrotes',
'aves', 'carnes', 'pescados']Data table
path = Path('../../output')
csv_files = L(path.glob('*.csv')).filter(lambda o: os.stat(o).st_size>0)
pat_store = re.compile('(.+)\_\d+')
pat_date = re.compile('.+\_(\d+)')
df = (
pd.concat([pd.read_csv(o).assign(store=pat_store.match(o.stem)[1], date=pat_date.match(o.stem)[1])
for o in csv_files], ignore_index=True)
.pipe(lambda d: d.assign(
name=d.name.str.lower()+' ('+d.store+')',
sku=d.id.where(d.sku.isna(), d.sku).astype(int),
date=pd.to_datetime(d.date)
))
.drop('id', axis=1)
.loc[lambda d: d.category.str.contains('|'.join(explore_types))]
# Filter products with recent data
# .loc[lambda d: d.name.isin(d.groupby('name').date.max().loc[ge(datetime.now()-timedelta(days=30))].index)]
# Filter empty prices
.loc[lambda d: d.price>0]
)
print(df.shape)
df.sample(3)(1298343, 8)
| sku | name | brand | category | uri | price | store | date | |
|---|---|---|---|---|---|---|---|---|
| 1429759 | 7541 | crema de ají tarí alacena doypack 85 g (metro) | AlaCena | https://www.metro.pe/abarrotes/fideos-pastas-y... | https://www.metro.pe/crema-de-aji-tari-alacena... | 5.90 | metro | 2022-12-01 |
| 1874329 | 10745958 | pack panetón con frutas bauducco caja 750g + g... | BAUDUCCO | https://www.plazavea.com.pe/panaderia-y-pastel... | NaN | 34.99 | plaza_vea | 2022-12-29 |
| 2261644 | 10567471 | nibs de cacao la casa marimiel doypack 200g (p... | LA CASA MARIMIEL | https://www.plazavea.com.pe/panaderia-y-pastel... | https://www.plazavea.com.pe/nibs-de-cacao-la-c... | 21.50 | plaza_vea | 2024-05-09 |
Top changes (ratio)
Code
top_changes = (df
# Use last 30 days of data to compare prices
.loc[lambda d: d.date>=(datetime.now()-timedelta(days=30))]
.sort_values('date')
# Get percentage change
.assign(change=lambda d: d
.groupby(['store','sku'], as_index=False)
.price.transform(lambda d: (d-d.shift())/d.shift())
)
.groupby(['store','sku'], as_index=False)
.agg({'price':'last', 'change':'mean', 'date':'last'})
.rename({'price':'last_price', 'date':'last_date'}, axis=1)
.dropna()
.loc[lambda d: d.last_date==d.last_date.max()]
.loc[lambda d: d.change.abs().sort_values(ascending=False).index]
)
top_changes.head(3)| store | sku | last_price | change | last_date | |
|---|---|---|---|---|---|
| 647 | plaza_vea | 3083 | 569.0 | 11.346774 | 2025-03-13 |
| 4818 | plaza_vea | 11358812 | 44.9 | 0.188393 | 2025-03-13 |
| 1109 | plaza_vea | 9402 | 32.9 | 0.159309 | 2025-03-13 |
Code
def plot_changes(df_changes, title):
selection = alt.selection_point(fields=['name'], bind='legend')
dff = df_changes.drop('change', axis=1).merge(df, on=['store','sku'])
return (dff
.pipe(alt.Chart)
.mark_line(point=True)
.encode(
x='date',
y='price',
color=alt.Color('name').scale(domain=sorted(dff.name.unique().tolist())),
tooltip=['name','price','last_price']
)
.add_params(selection)
.transform_filter(selection)
.interactive()
.properties(width=650, title=title)
.configure_legend(orient='top', columns=3)
)Code
top_changes.head(10).pipe(plot_changes, 'Top changes')Code
(top_changes
.sort_values('change')
.head(10)
.pipe(plot_changes, 'Top drops')
)Code
(top_changes
.sort_values('change')
.tail(10)
.pipe(plot_changes, 'Top increases')
)Top changes (absolute values)
Code
top_changes_abs = (df
# Use last 30 days of data to compare prices
.loc[lambda d: d.date>=(datetime.now()-timedelta(days=30))]
.sort_values('date')
# Get percentage change
.assign(change=lambda d: d
.groupby(['store','sku'], as_index=False)
.price.transform(lambda d: (d-d.shift()).iloc[-1])
)
.groupby(['store','sku'], as_index=False)
.agg({'price':'last', 'change':'mean', 'date':'last'})
.rename({'price':'last_price', 'date':'last_date'}, axis=1)
.dropna()
.loc[lambda d: d.last_date==d.last_date.max()]
.loc[lambda d: d.change.abs().sort_values(ascending=False).index]
)
top_changes_abs.head(3)| store | sku | last_price | change | last_date | |
|---|---|---|---|---|---|
| 4793 | plaza_vea | 11339782 | 105.0 | -49.0 | 2025-03-13 |
| 5341 | plaza_vea | 11501423 | 142.9 | 27.0 | 2025-03-13 |
| 5340 | plaza_vea | 11501422 | 129.9 | 20.0 | 2025-03-13 |
Code
top_changes_abs.head(10).pipe(plot_changes, 'Top changes')Code
(top_changes_abs
.sort_values('change')
.head(10)
.pipe(plot_changes, 'Top drops')
)Code
(top_changes_abs
.sort_values('change')
.tail(10)
.pipe(plot_changes, 'Top increases')
)Search specific products
Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Pollo')
.interactive()
.configure_legend(orient='top', columns=3)
)Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Palta')
.interactive()
.configure_legend(orient='top', columns=3)
)Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Aceite')
.interactive()
.configure_legend(orient='top', columns=3)
)Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Aceite')
.interactive()
.configure_legend(orient='top', columns=3)
)Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Aceite')
.interactive()
.configure_legend(orient='top', columns=3)
)